
from Flow import *
from Event import *
from Unit import *
from Scheduler import *

def getOracleFCT(flow, bandwidth):
    pd = PROPAGATION_DELAY
    td = (float(flow.flowSize) / bandwidth) # seconds
    return pd, td

def getSortedFlows(activeFlows):
    sortedActiveFlows = sorted(activeFlows, key=lambda flow: flow.remainTime)
    return sortedActiveFlows

class SchedulerIdeal(Scheduler):

    def __init__(self, numHosts, numSwitches, bandwidth):
        Scheduler.__init__(self)
        self.numHosts = numHosts
        self.numSwitches = numSwitches
        self.bandwidth = bandwidth
        self.srcPorts = {}
        self.dstPorts = {}
        for x in xrange(1, self.numHosts*self.numSwitches+1):
            self.srcPorts[x] = 0
            self.dstPorts[x] = 0

    def UpdateFlow(self, curFlow, eventType, eventQueue, curTime):
        # type: (object, object, object, object) -> object

        if eventType == FlowArrival:
            prop_delay, trans_delay = getOracleFCT(curFlow, self.bandwidth)
            curFlow.remainTime = trans_delay
            curFlow.oracleFct = prop_delay + trans_delay
            self.runningFlows.append(curFlow)
        elif eventType == FlowSourceFree:
            curFlow.finishSending = True
            curFlow.finishEvent = FlowDestFreeEvent(curTime+2*PROPAGATION_DELAY, curFlow)
            eventQueue.push(curFlow.finishEvent, curTime+2*PROPAGATION_DELAY)
        elif eventType == FlowDestFree:
            if not curFlow.finishSending:
                assert False
            self.runningFlows.remove(curFlow)
            curFlow.finishTime = curTime + 2*PROPAGATION_DELAY #send an ACK
            self.finishedFlows.append(curFlow)
        else:
            assert False

        for x in xrange(1, self.numHosts*self.numSwitches+1):    
            if self.srcPorts[x] != 0:
                inProgressFlow = self.srcPorts[x]
                if inProgressFlow.updateTime == 0:
                    print "flow in progress without LastTime set"
                    assert False
                if inProgressFlow.finishEvent == 0:
                    print "flow in progress without FinishEvent set"
                    assert False

                inProgressFlow.remainTime -= (curTime - inProgressFlow.updateTime)
                inProgressFlow.updateTime = 0

                if not inProgressFlow.finishSending:
                    inProgressFlow.finishEvent.cancelled = True
                    inProgressFlow.finishEvent = 0
                    
            self.srcPorts[x] = 0
            self.dstPorts[x] = 0

        sortedActiveFlows = getSortedFlows(self.runningFlows)
        for flow in sortedActiveFlows:
            src = flow.startId
            dst = flow.endId
            if flow.finishSending:
                if self.srcPorts[src] != 0 or self.dstPorts[dst] != 0:
                    print "ports taken on still sending flow"
                    assert False

                self.dstPorts[dst] = flow
                continue

            if self.srcPorts[src] == 0 and self.dstPorts[dst] == 0:
                #this flow gets scheduled.
                flow.updateTime = curTime
                self.srcPorts[src] = flow
                self.dstPorts[dst] = flow

                if flow.finishEvent != 0:
                    print "flow being scheduled, finish event non-nil"
                    assert False

                flow.finishEvent = FlowSourceFreeEvent(curTime+flow.remainTime, flow)
                eventQueue.push(flow.finishEvent, curTime+flow.remainTime)